/*
 * Copyright 2007-2008, www.jsemantic.org, www.adolfoestevez.com,
 * http://semanticj2ee.blogspot.com/ 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jservicerules.support;

import java.util.Collection;
import java.util.Map;

import org.jsemantic.core.context.external.ExternalContext;
import org.jsemantic.core.knowledgedbfactory.KnowledgeDBFactory;
import org.jsemantic.core.session.SemanticException;
import org.jsemantic.core.session.SemanticSession;
import org.jsemantic.core.session.factory.SemanticSessionFactory;
import org.jsemantic.servicerules.drools.session.factory.SemanticSessionFactoryImpl;
import org.jservicerules.support.annotations.interceptors.SemanticServiceInterceptor;

/**
 * <p>
 * Base class that can be inherited by services to get instances of the Semantic
 * Session without having to inject a SemanticSessionFactory. In other words, it
 * becomes a Semantic Service on the fly.<br>
 * 
 * <p>
 * Ways of configuring the SemanticService base class.<br>
 * 
 * <p> - Using just java code.<br>
 * 
 * The service is configured in a similar way that the SemanticSession Factory
 * would be, passing a rules file and the type of desired session in the
 * constructor and then setting the additional objects. A good example of this
 * is the {@link StatelessSemanticService} support class, that can be used to
 * execute simple set of rules files as a pure SemanticService.<br>
 * 
 * <p> - Using annotations.<br>
 * 
 * There is a complete set of annotations provided within this package to
 * annotate either a SemanticService or a SemanticSessionFactory.<br>
 * 
 * Example: <br>
 * <p>
 * <code> 
 * 
 * @SemanticService(rulesFile="org/jsemantic/support/examples/energy/tariff.drl")
 * 
 * @SpringExternalContext(contextFile="org/jsemantic/support/examples/energy/services.xml")
 * 
 * public class InvoiceService extends AbstractSemanticService {
 * ....
 * }
 * </code>
 * <br>
 * 
 * <p>
 * Of course that annotations must be processed in order to inject the
 * resources. There are two options for this: <br> - If the service is being
 * created by a spring context the only action needed is adding the class
 * {@link SemanticSessionBeanPostProcessor} to the context.<br>
 * <p>
 * <code>
 * 
 * 
 *<beans>
 *	<bean id="beanPostProcessor" class="org.jservicerules.support.spring.SemanticSessionBeanPostProcessor"/>
 *	<bean id="invoiceService" class="org.jsemantic.servicerules.examples.energy.services.InvoiceService"/>
 *</beans> 
 * </code>
 * <br>
 * 
 * <p> - If the service is being created just using code it would be needed to
 * call either {@link SemanticFactoryInterceptor} or
 * {@link SemanticServiceInterceptor} to process the annotations:<br>
 * 
 * <p>
 * 
 * <code>
 * 	MockService service= new MockService();
 * 
 *  SemanticServiceInterceptor interceptor = new SemanticServiceInterceptor();
 *
 *  interceptor.intercept(service);
 * </code> <br>
 * 
 */

public abstract class AbstractSemanticService {

	/** The Constant log. */
	private static final org.apache.log4j.Logger log = org.apache.log4j.Logger
			.getLogger(AbstractSemanticService.class);

	/** The semantic session factory. */
	private SemanticSessionFactory semanticSessionFactory = null;

	/**
	 * Instantiates a new abstract semantic service.
	 */
	public AbstractSemanticService() {
	}

	/**
	 * The Constructor.
	 * 
	 * @param rulesFile
	 *            the rules file
	 */
	public AbstractSemanticService(String rulesFile) {
		this.semanticSessionFactory = new SemanticSessionFactoryImpl(rulesFile,
				false);
	}

	/**
	 * The Constructor.
	 * 
	 * @param rulesFile
	 *            the rules file
	 * @param stateful
	 *            the stateful
	 */
	public AbstractSemanticService(String rulesFile, boolean stateful) {
		this.semanticSessionFactory = new SemanticSessionFactoryImpl(rulesFile,
				stateful);
	}

	/**
	 * Sets the semantic session factory.
	 * 
	 * @param semanticSessionFactory
	 *            the new semantic session factory
	 */
	public void setSemanticSessionFactory(
			SemanticSessionFactory semanticSessionFactory) {
		this.semanticSessionFactory = semanticSessionFactory;
	}

	/**
	 * Sets the knowledge db factory.
	 * 
	 * @param knowledgeFactory
	 *            the new knowledge db factory
	 */
	public void setKnowledgeDBFactory(KnowledgeDBFactory knowledgeFactory) {
		this.semanticSessionFactory.setKnowledgeDBFactory(knowledgeFactory);
	}

	/**
	 * Sets the external context.
	 * 
	 * @param ctx
	 *            the new external context
	 */
	public void setExternalContext(ExternalContext ctx) {
		this.semanticSessionFactory.setExternalContext(ctx);
	}

	/**
	 * Sets the session variables.
	 * 
	 * @param sessionVariables
	 *            the new session variables
	 */
	public void setSessionVariables(Map sessionVariables) {
		this.semanticSessionFactory.setSessionVariables(sessionVariables);
	}

	/**
	 * Gets an instance of a SemanticSession, execute a set of rules using a
	 * fact object and then dispose the session.
	 * 
	 * @param facts
	 *            the facts
	 * 
	 * @return the collection
	 */
	public Collection execute(Object facts) {

		SemanticSession session = getInstance();
		Collection results = null;
		try {
			results = session.execute(facts);
		} catch (SemanticException e) {
			log.error(e);
		} catch (Exception e) {
			log.error(e);
		} finally {
			if (session != null) {
				session.dispose();
				session = null;
			}
		}
		return results;
	}

	/**
	 * Gets an instance of a SemanticSession, execute a set of rules using a
	 * collection of facts objecs and then dispose the session.
	 * 
	 * @param facts
	 *            the facts
	 * 
	 * @return the collection
	 */
	public Collection execute(Collection facts) {

		SemanticSession session = getInstance();
		Collection results = null;
		try {
			results = session.execute(facts);
		} catch (SemanticException e) {
			log.error(e);
		} catch (Exception e) {
			log.error(e);
		} finally {
			if (session != null) {
				session.dispose();
				session = null;
			}
		}
		return results;
	}

	/**
	 * Returns and instance of the Semantic Session bound to the calling thread.
	 * 
	 * @return single instance of SemanticService
	 */
	protected SemanticSession getInstance() {
		return this.semanticSessionFactory.getInstance();
	}

}
